package com.translate;

import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.*;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 * @author by Administrator on 2018/1/18.
 */
public abstract class Excel2007Writer {
    private SpreadsheetWriter sw;

    /**
     * 写入电子表格的主要流程
     *
     * @param filePath
     * @throws Exception
     */
    public void process(String filePath, String fileName, List<Map<String, Object>> faildList, List<Map<String, Object>> mappingField) throws Exception {
        // 建立工作簿和电子表格对象
        XSSFWorkbook wb = new XSSFWorkbook();
        XSSFSheet sheet = wb.createSheet("sheet1");
        // 持有电子表格数据的xml文件名 例如 /xl/worksheets/sheet1.xml
        String sheetRef = sheet.getPackagePart().getPartName().getName();
        String templatePath = filePath + "template.xlsx";
        // 保存模板
        FileOutputStream os = new FileOutputStream(templatePath);
        wb.write(os);
        os.close();

        // 生成xml文件

        File tmp = File.createTempFile("sheet", ".xml");
        OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(tmp), "UTF-8");
        //写
        sw = new SpreadsheetWriter(fw);
        generate(faildList, mappingField);
        fw.close();


        // 使用产生的数据替换模板
        File templateFile = new File(templatePath);
        String fullPath = filePath + fileName;
        FileOutputStream out = new FileOutputStream(fullPath);
        substitute(templateFile, tmp, sheetRef.substring(1), out);
        out.close();
        //删除文件之前调用一下垃圾回收器，否则无法删除模板文件
        //  System.gc();
        // 删除临时模板文件
        if (templateFile.isFile() && templateFile.exists()) {
            templateFile.delete();
        }
    }

    /**
     * 在写入器中写入电子表格
     */
    public static class SpreadsheetWriter {
        private final Writer out;
        private int rownum;
        private static String LINE_SEPARATOR = System.getProperty("line.separator");

        public SpreadsheetWriter(Writer out) {
            this.out = out;
        }

        public void beginSheet() throws IOException {
            out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                    + "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">");
            out.write("<sheetData>" + LINE_SEPARATOR);
        }

        public void endSheet() throws IOException {
            out.write("</sheetData>");
            out.write("</worksheet>");
        }

        /**
         * 插入新行
         *
         * @param rownum 以0开始
         */
        public void insertRow(int rownum) throws IOException {
            out.write("<row r=\"" + (rownum + 1) + "\">" + LINE_SEPARATOR);
            this.rownum = rownum;
        }

        /**
         * 插入行结束标志
         */
        public void endRow() throws IOException {
            out.write("</row>" + LINE_SEPARATOR);
        }

        /**
         * 插入新列
         *
         * @param columnIndex
         * @param value
         * @param styleIndex
         * @throws IOException
         */
        public void createCell(int columnIndex, String value, int styleIndex)
                throws IOException {
            String ref = new CellReference(rownum, columnIndex)
                    .formatAsString();
            out.write("<c r=\"" + ref + "\" t=\"inlineStr\"");
            if (styleIndex != -1) {
                out.write(" s=\"" + styleIndex + "\"");
            }
            out.write(">");
            out.write("<is><t>" + XMLEncoder.encode(value) + "</t></is>");
            out.write("</c>");
        }
    }

    private static void copyStream(InputStream in, OutputStream out)
            throws IOException {
        byte[] chunk = new byte[1024];
        int count;
        while ((count = in.read(chunk)) >= 0) {
            out.write(chunk, 0, count);
        }
    }

    /**
     * @param zipfile the template file
     * @param tmpfile the XML file with the sheet data
     * @param entry   the name of the sheet entry to substitute, e.g. xl/worksheets/sheet1.xml
     * @param out     the stream to write the result to
     */
    private static void substitute(File zipfile, File tmpfile, String entry,
                                   FileOutputStream out) throws IOException {
        ZipFile zip = new ZipFile(zipfile);
        ZipOutputStream zos = new ZipOutputStream(out);

        @SuppressWarnings("unchecked")
        Enumeration<ZipEntry> en = (Enumeration<ZipEntry>) zip.entries();
        while (en.hasMoreElements()) {
            ZipEntry ze = en.nextElement();
            if (!ze.getName().equals(entry)) {
                zos.putNextEntry(new ZipEntry(ze.getName()));
                InputStream is = zip.getInputStream(ze);
                copyStream(is, zos);
                is.close();
            }
        }
        zos.putNextEntry(new ZipEntry(entry));
        InputStream is = new FileInputStream(tmpfile);
        copyStream(is, zos);
        is.close();
        zos.close();
    }

    /**
     * 类使用者应该使用此方法进行写操作
     *
     * @throws Exception
     */
    public abstract void generate(List<Map<String, Object>> faildList, List<Map<String, Object>> mappingField) throws Exception;

    public void beginSheet() throws IOException {
        sw.beginSheet();
    }

    public void insertRow(int rowNum) throws IOException {
        sw.insertRow(rowNum);
    }

    public void createCell(int columnIndex, String value) throws IOException {
        sw.createCell(columnIndex, value, -1);
    }

    public void endRow() throws IOException {
        sw.endRow();
    }

    public void endSheet() throws IOException {
        sw.endSheet();
    }
}
